import {
  ChangeDetectorRef,
  EventEmitter,
  Input,
  OnChanges,
  OnDestroy,
  OnInit,
  Output,
  SimpleChanges,
  ViewChild,
} from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
import { ApesNoAnimationDirective } from '@shared/apes/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { InputBoolean } from '../core/util/convert';
import { DateHelperService, ApesDatePickerI18nInterface, ApesI18nService } from '@shared/apes/i18n';
import { ApesPickerComponent } from './picker.component';
import { CandyDate } from './lib/candy-date/candy-date';
const POPUP_STYLE_PATCH = { 'position': 'relative' }; // Aim to override antd's style to support overlay's position strategy (position:absolute will cause it not working beacuse the overlay can't get the height/width of it's content)

/**
 * The base picker for all common APIs
 */
export abstract class AbstractPickerComponent implements OnInit, OnChanges,OnDestroy, ControlValueAccessor {
  // --- Common API
  @Input() @InputBoolean() apesAllowClear: boolean = true;
  @Input() @InputBoolean() apesAutoFocus: boolean = false;
  @Input() @InputBoolean() apesDisabled: boolean = false;
  @Input() @InputBoolean() apesOpen: boolean;
  @Input() apesClassName: string;
  @Input() apesDisabledDate: (d: Date) => boolean;
  @Input() apesLocale: ApesDatePickerI18nInterface;
  @Input() apesPlaceHolder: string | string[];
  @Input() apesPopupStyle: object = POPUP_STYLE_PATCH;
  @Input() apesDropdownClassName: string;
  @Input() apesSize: 'large' | 'small';
  @Input() apesStyle: object;
  @Input() apesFormat: string;
  @Input() apesValue: CompatibleValue | null;

  @Output() apesOnOpenChange = new EventEmitter<boolean>();

  @ViewChild(ApesPickerComponent,{static:true}) protected picker: ApesPickerComponent;

  isRange: boolean = false; // Indicate whether the value is a range value

  protected destroyed$: Subject<void> = new Subject();
  protected isCustomPlaceHolder: boolean = false;

  get realOpenState(): boolean {
    return this.picker.animationOpenState;
  } // Use picker's real open state to let re-render the picker's content when shown up

  initValue(): void {
    this.apesValue = this.isRange ? [] : null;
  }

  constructor(protected i18n: ApesI18nService,
              protected cdr: ChangeDetectorRef,
              protected dateHelper: DateHelperService,
              public noAnimation?: ApesNoAnimationDirective) {
  }

  ngOnInit(): void {
    // Default locale (NOTE: Place here to assign default value due to the i18n'locale may change before ngOnInit)
    if (!this.apesLocale) {
      this.i18n.localeChange.pipe(takeUntil(this.destroyed$)).subscribe(() => this.setLocale());
    }

    // Default value
    this.initValue();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.apesPopupStyle) { // Always assign the popup style patch
      this.apesPopupStyle = this.apesPopupStyle ? { ...this.apesPopupStyle, ...POPUP_STYLE_PATCH } : POPUP_STYLE_PATCH;
    }
    if (changes.apesPlaceHolder && changes.apesPlaceHolder.firstChange && typeof this.apesPlaceHolder !== 'undefined') {
     this.isCustomPlaceHolder = true;
   }
    if (changes.apesLocale) { // The nzLocale is currently handled by user
      this.setDefaultPlaceHolder();
   }
 }
  ngOnDestroy(): void {
    this.destroyed$.next();
    this.destroyed$.complete();
  }

  closeOverlay(): void {
    this.picker.hideOverlay();
  }

  /**
   * Common handle for value changes
   * @param value changed value
   */
  onValueChange(value: CompatibleValue): void {
    this.apesValue = value;
    if (this.isRange) {
      const vAsRange = this.apesValue as CandyDate[];
      if (vAsRange.length) {
        this.onChangeFn([vAsRange[0].getNativeDate(), vAsRange[1].getNativeDate()]);
      } else {
        this.onChangeFn([]);
      }
    } else {
      if (this.apesValue) {
        this.onChangeFn((this.apesValue as CandyDate).getNativeDate());
      } else {
        this.onChangeFn(null);
      }
    }
    this.onTouchedFn();
  }

  /**
   * Triggered when overlayOpen changes (different with realOpenState)
   * @param open The overlayOpen in picker component
   */
  onOpenChange(open: boolean): void {
    this.apesOnOpenChange.emit(open);
  }

  // ------------------------------------------------------------------------
  // | Control value accessor implements
  // ------------------------------------------------------------------------

  // NOTE: onChangeFn/onTouchedFn will not be assigned if user not use as ngModel
  onChangeFn: (val: CompatibleDate | null) => void = () => void 0;
  onTouchedFn: () => void = () => void 0;

  writeValue(value: CompatibleDate): void {
    this.setValue(value);
    this.cdr.markForCheck();
  }

  registerOnChange(fn: any): void { // tslint:disable-line:no-any
    this.onChangeFn = fn;
  }

  registerOnTouched(fn: any): void { // tslint:disable-line:no-any
    this.onTouchedFn = fn;
  }

  setDisabledState(disabled: boolean): void {
    this.apesDisabled = disabled;
    this.cdr.markForCheck();
  }

  // ------------------------------------------------------------------------
  // | Internal methods
  // ------------------------------------------------------------------------
  private setLocale(): void {
    this.apesLocale = this.i18n.getLocaleData('DatePicker', {});
    this.setDefaultPlaceHolder();
    this.cdr.markForCheck();
  }
  private setDefaultPlaceHolder(): void {
    if (!this.isCustomPlaceHolder && this.apesLocale) {
       this.apesPlaceHolder = this.isRange ? this.apesLocale.lang.rangePlaceholder : this.apesLocale.lang.placeholder;
    }
   }

  // Safe way of setting value with default
  private setValue(value: CompatibleDate): void {
    if (this.isRange) {
      this.apesValue = value ? (value as Date[]).map(val => new CandyDate(val)) : [];
    } else {
      this.apesValue = value ? new CandyDate(value as Date) : null;
    }
  }
}

export type CompatibleValue = CandyDate | CandyDate[];

export type CompatibleDate = Date | Date[];
